package hw2.memdir;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.Serializable;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;

/**
 * 
 * used by EncList.java to create the encrypted directory objects
 * 
 */
public class Directory implements Serializable
{

    private static final long serialVersionUID = -8330479199399585497L;

    // IV used for encryption of the last names
    private byte[] iv0;

    /**
     * map from the encrypted last name to the IV for the email
     */
    private ConcurrentHashMap<String, byte[]> nameToIV = new ConcurrentHashMap<String, byte[]>();

    /**
     * map from the encrypted last name to the encrypted email
     */
    private ConcurrentHashMap<String, byte[]> nameToEmail = new ConcurrentHashMap<String, byte[]>();

    /**
     * the symmetric key used for encryption and decryption
     */
    private SecretKey aesKey;

    /**
     * 
     * @param plaintextDir
     *            a plain text file containing a list of last names and email
     *            addresses
     * @param password
     *            the password to be used for creation of the AES key
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */

    public Directory( File plaintextDir, String password )
            throws NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidKeyException, IllegalBlockSizeException, BadPaddingException {

        // Generate the aesKey using salt and the password
        aesKey = KeyGen.getAESkey( password );
        Cipher aesNameEnc = Cipher.getInstance( "AES/CBC/PKCS5Padding" );
        aesNameEnc.init( Cipher.ENCRYPT_MODE, aesKey );
        this.iv0 = aesNameEnc.getIV();

        // open the plain text file
        Scanner in = null;
        String line[];
        try {
            in = new Scanner( plaintextDir );
        } catch( FileNotFoundException e ) {
            e.printStackTrace();
        }

        // skip the header line
        if( in.hasNextLine() ) {
            in.nextLine();
        }

        // read each line, encrypting the name and email separately, using a new
        // IV for each email
        String name = null;
        String email = null;
        while( in.hasNextLine() ) {
            // split the line on any whitespace characters
            line = (in.nextLine().toLowerCase()).split( "\\s+" );

            name = line[0].toLowerCase();
            email = line[1].toLowerCase();

            // encrypt the name
            byte[] encName = aesNameEnc.doFinal( name.getBytes() );

            // encrypt the email
            Cipher aesEmailEnc = Cipher.getInstance( "AES/CBC/PKCS5Padding" );
            aesEmailEnc.init( Cipher.ENCRYPT_MODE, aesKey );

            // get the new iv used for this email
            byte[] iv = aesEmailEnc.getIV();
            byte[] encEmail = aesEmailEnc.doFinal( email.getBytes() );

            // store the encrypted name to encrypted email
            this.nameToEmail.put( new String( encName ), encEmail );

            // store the encrypted name to IV
            this.nameToIV.put( new String( encName ), iv );
        }
    }

    /**
     * 
     * @param name
     *            lookup the encrypted name and get the right IV
     * @return
     */
    public byte[] getIV( byte[] name ) {
        return this.nameToIV.get( new String( name ) );
    }

    /**
     * 
     * @param name
     *            lookup the encrypted name and get the encrypted email
     * @return
     */
    public byte[] getEncryptedEmail( byte[] name ) {
        return this.nameToEmail.get( new String( name ) );
    }

    /**
     * 
     * @return the IV used for the last names
     */
    public byte[] getNameIV() {
        return this.iv0;
    }
}
